#include "jsmf.h"
#include "string.h"
#include <stdio.h>

static jsmf_creater creater;

static inline uint8_t StackCount()
{
    return creater.stacki;
}

static inline uint8_t TopStack()
{
    return creater.tokStack[creater.stacki - 1];
}

static inline void PushStack(uint8_t v)
{
    creater.tokStack[creater.stacki++] = v;
}

static inline uint8_t PopStack()
{
    return creater.tokStack[creater.stacki--];
}

int AllocFreeTok(jsmftok_t **tok)
{
    int tokIndex = creater.tokNext;
    if (tokIndex >= creater.tokNum) {
        *tok = NULL;
        return -1;
    }
    *tok = &creater.tokBuf[creater.tokNext++];
    memset(*tok, 0, sizeof(jsmftok_t));
    (*tok)->parent = 0xff;
    return tokIndex;
}

#define MAKE_TOK(tok, n, t, p) \
    do {                       \
        if (!tok)              \
            return -1;         \
        (tok)->name = (n);     \
        (tok)->type = (t);     \
        (tok)->parent = (p);   \
    } while (0)

jsmf_obj JsmfInitRoot(jsmftok_t *tokBuf, uint8_t numToks)
{
    memset(&creater, 0, sizeof(creater));
    memset(tokBuf, 0, numToks * sizeof(jsmftok_t));
    creater.tokBuf = tokBuf;
    creater.tokNum = numToks;
    jsmftok_t *tok = NULL;
    creater.tokRoot = AllocFreeTok(&tok);
    if (tok != NULL) {
        tok->type = JSMF_OBJECT;
    }
    return creater.tokRoot;
}

int AddObjToObject(jsmf_obj object, char *name)
{
    jsmftok_t *tok = NULL;
    int tokIndex = AllocFreeTok(&tok);
    MAKE_TOK(tok, name, JSMF_OBJECT, object);
    return tokIndex;
}

int AddObjToArray(jsmf_obj object)
{
    jsmftok_t *tok = NULL;
    int tokIndex = AllocFreeTok(&tok);
    MAKE_TOK(tok, NULL, JSMF_OBJECT, object);
    return tokIndex;
}

int AddArrToObject(jsmf_obj object, char *name)
{
    jsmftok_t *tok = NULL;
    int tokIndex = AllocFreeTok(&tok);
    MAKE_TOK(tok, name, JSMF_ARRAY, object);
    return tokIndex;
}

int AddStringToObject(jsmf_obj object, char *name, char *string)
{
    jsmftok_t *tok = NULL;
    int tokIndex = AllocFreeTok(&tok);
    MAKE_TOK(tok, name, JSMF_STRING, object);
    tok->str = string;
    return tokIndex;
}

int AddNumToObject(jsmf_obj object, char *name, const int number)
{
    jsmftok_t *tok = NULL;
    int tokIndex = AllocFreeTok(&tok);
    MAKE_TOK(tok, name, JSMF_PRIMITIVE, object);
    tok->num = number;
    return tokIndex;
}

int AddStringToArray(jsmf_obj object, char *string)
{
    jsmftok_t *tok = NULL;
    int tokIndex = AllocFreeTok(&tok);
    MAKE_TOK(tok, NULL, JSMF_STRING, object);
    tok->str = string;
    return tokIndex;
}

int AddNumToArray(jsmf_obj object, const int number)
{
    jsmftok_t *tok = NULL;
    int tokIndex = AllocFreeTok(&tok);
    MAKE_TOK(tok, NULL, JSMF_PRIMITIVE, object);
    tok->num = number;
    return tokIndex;
}

int AddEndOfObj(jsmf_obj object, char *str)
{
    jsmftok_t *tok = NULL;
    int tokIndex = AllocFreeTok(&tok);
    MAKE_TOK(tok, NULL, JSMF_END_TOK, object);
    tok->str = str;
    return tokIndex;
}

static void PrintTok(int now, int pre, char *buf, int bufLen)
{
    if (buf == NULL || bufLen <= 0) {
        return;
    }
    char *dest = buf + strlen(buf);
    int remLen = bufLen - strlen(buf);

    if ((pre != -1) && (creater.tokBuf[now].type != JSMF_END_TOK)) {
        if ((creater.tokBuf[pre].type != JSMF_OBJECT) && (creater.tokBuf[pre].type != JSMF_ARRAY)) {
            sprintf(dest, "%s", ",");
            dest = dest + 1;
        }
    }

    switch (creater.tokBuf[now].type) {
        case JSMF_STRING:
            if (creater.tokBuf[now].name == NULL) {
                sprintf(dest, "\"%s\"", creater.tokBuf[now].str);
            } else {
                sprintf(dest, "\"%s\":\"%s\"", creater.tokBuf[now].name, creater.tokBuf[now].str);
            }
            break;
        case JSMF_PRIMITIVE:
            if (creater.tokBuf[now].name == NULL) {
                sprintf(dest, "%d", creater.tokBuf[now].num);
            } else {
                sprintf(dest, "\"%s\":%d", creater.tokBuf[now].name, creater.tokBuf[now].num);
            }
            break;
        case JSMF_ARRAY:
            if (creater.tokBuf[now].name != NULL) {
                sprintf(dest, "\"%s\":[", creater.tokBuf[now].name);
            }
            break;
        case JSMF_OBJECT:
            if (creater.tokBuf[now].name != NULL) {
                sprintf(dest, "\"%s\":{", creater.tokBuf[now].name);
            } else {
                sprintf(dest, "%s", "{");
            }
            break;
        case JSMF_END_TOK:
            sprintf(dest, "%s", creater.tokBuf[now].str);
            break;
        default:
            break;
    }
}

int PrintJsonStr(jsmf_obj object, char *buf, int len)
{
    int now;
    int pre = -1;
    int tokNo = creater.tokNext;
    memset(buf, 0, len);
    for (uint8_t i = 0; i < tokNo; i++) {
        if (creater.tokBuf[i].type == JSMF_OBJECT) {
            AddEndOfObj(i, "}");
        } else if (creater.tokBuf[i].type == JSMF_ARRAY) {
            AddEndOfObj(i, "]");
        }
    }

    PushStack(object);
    while (StackCount() != 0) {
        now = TopStack();
        PopStack();
        PrintTok(now, pre, buf, len);
        pre = now;
        for (int8_t i = creater.tokNext - 1; i >= 0; i--) {
            if (creater.tokBuf[i].parent == now) {
                PushStack(i);
            }
        }
    }
    return 0;
}
